using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Common {
	public class CommandManager<T> : IRegisterManager<T> where T : Command {
		readonly Dictionary<string, T> commands = new Dictionary<string, T>();
		public void Register(T command) {
			if (IsExists(command.Id)) {
				throw new ArgumentException("A command with the same ID: " + command.Id + " is already registered!");
			}
			commands.Add(command.Id, command);
		}
		public bool IsExists(string id) => commands.ContainsKey(id);
		public T Get(string id) {
			if (!commands.ContainsKey(id)) {
				return null;
			}
			return commands[id];
		}
		public void Remove(string id) => commands.Remove(id);
		public void Clear() => commands.Clear();
		public T this[string id]=>Get(id);
	}

	public abstract class Command : IRegisterable {
		public static (string id, ComplexValue[] arguments) Parse(string command) {
			string cmd = "";
			List<ComplexValue> arguments = new List<ComplexValue>();
			int index = 0;
			string text = "";
			bool stringMode = false;

			while (index < command.Length) {
				string current = command[index].ToString();
				index += 1;

				if (current == "\"" && !stringMode) {
					stringMode = true;
					current = "";
				} else if (current == @"\" && stringMode) {
					current = command[index].ToString();
					index += 1;
					if (current == @"\") {
						text += @"\";
					} else if (current == "\"") {
						text += "\"";
						current = "";
					}
				} else if (!((current == " " && !stringMode) || (current == "\"" && stringMode))) {
					text += current;
				}

				if ((current == " " && !stringMode) || (current == "\"" && stringMode) || index >= command.Length) {
					if (string.IsNullOrEmpty(cmd)) {
						cmd = text;
					} else if (stringMode) {
						arguments.Add(text);
					} else if (!string.IsNullOrEmpty(text)) {
						arguments.Add(ParseString(text));
					}
					text = "";
					stringMode = false;
				}
			}
			return (cmd, arguments.ToArray());
		}

		static ComplexValue ParseString(string text) {
			if (Regex.IsMatch(text, @"^[+-]?[0-9]+$")) {
				if (int.TryParse(text, out int result)) {
					return result;
				}
			} else if (Regex.IsMatch(text, @"^[+-]?[0-9]+(\.[0-9]+)?([Ee][+-]?[0-9]+)?$")) {
				return float.Parse(text);
			}
			return text;
		}

		public enum ArgumentValidateResult {
			OK,
			InvalidType,
			InvalidCount
		}
		public static (ArgumentValidateResult result, int index) ValidateArguments(Command command, ComplexValue[] arguments) {
			int requiredCount = command.GetRequiredArgumentCount();
			if (arguments.Length < requiredCount) {
				return (ArgumentValidateResult.InvalidCount, -1);
			}
			for (int i = 0; i < command.Arguments.Length; i += 1) {
				bool valid = false;
				Argument.Type rType = command.Arguments[i].RequiredType;
				ComplexValue.Type type = arguments[i].CurrentType;

				if (!valid && rType.HasFlag(Argument.Type.Integer)) {
					valid = (type != ComplexValue.Type.String && type != ComplexValue.Type.Float);
				}
				if (!valid && rType.HasFlag(Argument.Type.Float)) {
					valid = (type != ComplexValue.Type.String);
				}
				if (!valid && rType.HasFlag(Argument.Type.String)) {
					valid = true;
				}
				if (!valid) {
					return (ArgumentValidateResult.InvalidType, i);
				}
			}
			return (ArgumentValidateResult.OK, -1);
		}

		public static ComplexValue[] FormatArguments(Command command, ComplexValue[] arguments) {
			ComplexValue[] args = new ComplexValue[command.Arguments.Length];
			arguments.CopyTo(args, 0);

			for (int i = arguments.Length; i < command.Arguments.Length; i += 1) {
				args[i] = command.Arguments[i].DefaultValue;
			}
			return args;
		}

		public struct Argument {
			public Argument(string name, Type type, bool required = true, ComplexValue defaultValue = null) {
				Name = name;
				RequiredType = type;
				Required = required;
				DefaultValue = defaultValue ?? ComplexValue.Null;
			}
			[Flags]
			public enum Type {
				Integer = 0x0001,
				Float = 0x0010,
				String = 0x0100,
			}
			public string Name { get; }
			public Type RequiredType { get; }
			public bool Required { get; }
			public ComplexValue DefaultValue { get; }
		}

		public abstract string Id { get; }
		public virtual Argument[] Arguments { get; set; } = Array.Empty<Argument>();
		public int GetRequiredArgumentCount() {
			int count = 0;
			for (int i = 0; i < Arguments.Length; i += 1) {
				if (!Arguments[i].Required) {
					break;
				}
				count += 1;
			}
			return count;
		}
	}
}
